package qiniu

import (
	"crypto/md5"
	"encoding/hex"
	"fmt"
	"net/url"
	"reflect"
	"strconv"
	"strings"
	"time"
	"unsafe"
)

// GenUrlWithSignTime 生成带七牛云时间戳防盗链签名的url
// @key 密钥key
// @baseUrl 视频播放地址
// @duration 签名有效期
// @return 含签名和时间的播放地址 错误信息
func GenUrlWithSignTime(key, baseUrl string, duration time.Duration) (string, error) {
	return genUrlWithSignTime(key, baseUrl, time.Now().Add(duration))
}

func genUrlWithSignTime(key, baseUrl string, t time.Time) (string, error) {
	u, err := url.Parse(baseUrl)
	if err != nil {
		return "", err
	}
	pathEncode := strings.ReplaceAll(url.PathEscape(u.Path), "%2F", "/")
	expiredAtX := strconv.FormatInt(t.Unix(), 16)
	rawStr := fmt.Sprintf("%s%s%s", key, pathEncode, expiredAtX)

	md5Str, err := Md5String(rawStr)
	if err != nil {
		return "", err
	}
	q := fmt.Sprintf("sign=%s", md5Str) + fmt.Sprintf("&t=%s", expiredAtX)
	if u.RawQuery != "" {
		u.RawQuery = q + "&" + u.RawQuery
	} else {
		u.RawQuery = q
	}
	return u.String(), nil
}

func Md5String(data string) (string, error) {
	hash := md5.New()
	_, err := hash.Write(StringToBytes(data))
	if err != nil {
		return "", err
	}
	return hex.EncodeToString(hash.Sum(nil)), nil
}

func Md5Bytes(data []byte) (string, error) {
	hash := md5.New()
	_, err := hash.Write(data)
	if err != nil {
		return "", err
	}
	return hex.EncodeToString(hash.Sum(nil)), nil
}

// StringToBytes converts a string to bytes without copy.
func StringToBytes(s string) (b []byte) {
	h := (*reflect.SliceHeader)(unsafe.Pointer(&b))
	h.Data = (*reflect.StringHeader)(unsafe.Pointer(&s)).Data
	h.Len = len(s)
	h.Cap = len(s)
	return
}

// BytesToString converts a byte array to string without copy.
func BytesToString(b []byte) (s string) {
	h := (*reflect.StringHeader)(unsafe.Pointer(&s))
	h.Data = (*reflect.SliceHeader)(unsafe.Pointer(&b)).Data
	h.Len = len(b)
	return
}
